# @Time : 2021/4/3 16:29
# @Author : Fioman 
# @Phone : 13149920693
"""
1. 模块说明: 裁切机控制卡驱动模块
2. 模块功能: 主要用来和裁切机控制卡进行通信
"""
from ctypes import *
import gevent
from settings import ZMC_LIB_PATH, AUX_ZMC_LIB_PATH
from tools.hd_logger import HdLogger
from db_tools.db_handler import DbHandler
from tools.fioman_math_tools import get_line_data_with_capflie, get_line_data_without_capfile
import numpy as np

# 裁切机参数的索引名称
cbjZmcParamsKey = [
    "fx_max",  # 1
    "fx_equivalent",  # 2
    "fx_speed_min",  # 3
    "fx_speed_max",  # 4
    "fx_work_speed",  # 5
    "fx_acc_speed",  # 6
    "fx_dec_speed",  # 7
    "fx_s_shape_curve",  # 8
    "fx_to_zero_speed",  # 9
    "fx_to_zero_acc_speed",  # 10
    "fx_to_zero_dec_speed",  # 11
    "fy_max",  # 12
    "fy_equivalent",  # 13
    "fy_speed_min",  # 14
    "fy_speed_max",  # 15
    "fy_work_speed",  # 16
    "fy_acc_speed",  # 17
    "fy_dec_speed",  # 18
    "fy_s_shape_curve",  # 19
    "fy_to_zero_speed",  # 20
    "fy_to_zero_acc_speed",  # 21
    "fy_to_zero_dec_speed",  # 22
    "fz_max",  # 23
    "fz_equivalent",  # 24
    "fz_speed_min",  # 25
    "fz_speed_max",  # 26
    "fz_work_speed",  # 27
    "fz_acc_speed",  # 28
    "fz_dec_speed",  # 29
    "fz_s_shape_curve",  # 30
    "fz_to_zero_speed",  # 31
    "fz_to_zero_acc_speed",  # 32
    "fz_to_zero_dec_speed",  # 33
    "fr_max",  # 34
    "fr_equivalent",  # 35
    "fr_speed_min",  # 36
    "fr_speed_max",  # 37
    "fr_work_speed",  # 38
    "fr_acc_speed",  # 39
    "fr_dec_speed",  # 40
    "fr_s_shape_curve",  # 41
    "fr_to_zero_speed",  # 42
    "fr_to_zero_acc_speed",  # 43
    "fr_to_zero_dec_speed",  # 44
    "cut_speed_min",  # 45
    "cut_speed_max",  # 46
    "cut_speed_work",  # 47
    "cut_acc_speed",  # 48
    "cut_dec_speed",  # 49
    "cut_s_shape_curve",  # 50
    "drag_speed",  # 51
    "drag_acc_speed",  # 52
    "drag_dec_speed"  # 53
]


class CbjZmc(object):
    """
    裁切机控制器驱动类
    """

    def __init__(self):
        self.handleRobot = c_longlong()
        self.logger = HdLogger()
        self.db = DbHandler()
        self.errorMsg = ""
        self.tryAgainDuraion = 0.5  # 通信失败,重新试一遍的时间间隔
        self.ip = "192.168.0.12"
        self.tableName = "cbjZmcParams"
        self.load_cafile_data_params()

        try:
            self.api = cdll.LoadLibrary(ZMC_LIB_PATH)
            self.aux_api = cdll.LoadLibrary(AUX_ZMC_LIB_PATH)
        except Exception as e:
            self.logger.debug("in CbjZmc.__init__() error : {}".format(str(e)))
            self.api = None
            self.aux_api = None

    def __del__(self):
        """
        对象被销毁的时候调用次方法.
        :return:
        """
        try:
            self.close_zmc_conn()
        except Exception as e:
            errorInfo = "in CbjZmc.__init__() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

    def load_cafile_data_params(self):
        """
        capfileData的数据初始化
        :return:
        """
        # 台布测高的数据获取.
        res, capfileData = self.db.get_capfile_data()
        if res != "ok":
            self.errorMsg = capfileData
        else:
            self.capfileData = capfileData.get("capfileData")
            self.xMin = capfileData.get("xMin")
            self.yMin = capfileData.get("yMin")
            self.xInterval = capfileData.get("xInterval")
            self.yInterval = capfileData.get("yInterval")
            self.xNums = capfileData.get("xNums")
            self.yNums = capfileData.get("yNums")

    def check_connect(self):
        try:
            isConnect = self.get_modbus_bit(0, 1)[0]
            isConnect = True if isConnect == 0 else False
            return isConnect
        except Exception as e:
            errorInfo = "in CbjZmc.check_connect() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
            return False

    def cbj_zmc_start(self):
        """
        裁切机控制器启动
        :return:
        """
        bitPos = 0
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in cbj_zmc_start() 设置 modbus_bit({}) = {} 失败. ".format(bitPos, bitValue)
            return self.errorMsg
        return ret

    def cbj_zmc_stop(self):
        """
        裁切机控制器停止
        :return:
        """
        bitPos = 1
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in cbj_zmc_stop() 设置 modbus_bit({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def cbj_zmc_rs_stop(self):
        """
        裁切机控制器设备急停
        :return:
        """
        bitPos = 2
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in cbj_zmc_rs_stop() 设置 modbus_bit({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def cbj_zmc_reset(self):
        """
        裁切机控制器复位
        :return:
        """
        bitPos = 3
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in cbj_zmc_reset() 设置 modbus_bit({}) = {} 失败!".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def is_running(self):
        """
        获取上料控制器是否是运行状态
        :return:
        """
        bitPos = 100
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in is_running() 读取 modbus_bit ({}) 失败".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def is_stop_state(self):
        """
        获取上料控制器是否是停止状态
        :return:
        """
        bitPos = 101
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in is_stop_state() 读取 modbus_bit ({}) 失败".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def is_rs_stop(self):
        """
        获取上料控制器是否是急停状态
        :return:
        """
        bitPos = 102
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in is_rs_stop() 读取 modbus_bit ({}) 失败".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def is_reset_state(self):
        """
        获取上料控制器是否是复位状态
        :return:
        """
        bitPos = 103
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in is_reset_state() 读取 modbus_bit ({}) 失败".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def get_allow_send_lines(self):
        """
        获取是否可以发送切线的函数
        :return:
        """
        bitPos = 25
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in get_allow_send_lines() 读取 modbus_bit ({}) 失败".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def set_cut_lines_send_finish(self):
        """
        设置切线发送完毕
        :return:
        """
        bitPos = 22
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in set_cut_lines_send_finish() 设置 modbus_bit({}) = {} 失败!".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def set_third_cut_needed(self, isCut):
        """
        设置是否切第三边
        :return:
        """
        bitPos = 26
        bitValue = 1 if isCut else 0
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in set_third_cut_needed() 设置 modbus_bit({}) = {} 失败!".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def get_drag_begin_state(self):
        """
        获取拖拽开始的信号,裁切机拖拽开始,然后铡刀那里的数据开始更新
        :return:
        """
        bitPos = 30
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in get_drag_begin_state() 读取 modbus_bit ({}) 失败".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def start_test_plat_form(self):
        """
        设置开始测高
        :return:
        """
        bitPos = 19
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in start_test_plat_form() 设置 modbus_bit({}) = {} 失败!".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def set_update_cutter_state_finish(self):
        """
        设置铡刀的数据更新完成,铡刀可以开始工作的代码逻辑
        :return:
        """
        bitPos = 31
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in set_update_cutter_state_finish() 设置 modbus_bit({}) = {} 失败!".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def set_table(self, start, num, table):
        """
        将数据写入到寄存器的表中
        :param start:表格的起始位置
        :param num:写入的值个数
        :param table:写入的值列表
        :return:
        """
        value = (c_float * num)(0)
        value[:] = table
        self.aux_api.ZAux_Direct_SetTable.restype = c_int32
        self.aux_api.ZAux_Direct_SetTable.argtype = (c_void_p, c_uint32, c_uint32, c_void_p)

        try:
            res = self.aux_api.ZAux_Direct_SetTable(self.handleRobot, c_int32(start), c_int32(num), value)
        except Exception as e:
            res = -1
            errorInfo = "in CbjZmc.set_table()  error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
        if res != 0:
            self.errorMsg = "in CbjZmc.set_table() ZAux_Direct_GetTable() 执行失败"
        return res

    def get_table(self, start, num):
        """
        从底层的表中读取数据table表
        :param start: 读取的起始位置
        :param num: 读取的数据表的个数
        :return:
        """
        value = (c_float * num)()
        self.aux_api.ZAux_Direct_GetTable.restype = c_int32
        self.aux_api.ZAux_Direct_GetTable.argtype = (c_void_p, c_int32, c_int32, c_void_p)
        try:
            res = self.aux_api.ZAux_Direct_GetTable(self.handleRobot, c_int32(start), c_int32(num), value)
        except Exception as e:
            res = -1
            errorInfo = "in CbjZmc.get_table() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
        if res != 0:
            self.errorMsg = "in CbjZmc.get_table() ZAux_Direct_GetTable 执行失败"

        return res, value[:]

    def get_output(self, portPos):
        """
        获取输出口的状态
        :param portPos:
        :return: 结果码:0后者其他 值也是0或者1
        """
        value = (c_uint32 * 1)()
        self.aux_api.ZAux_Direct_GetOp.restype = c_int32
        self.aux_api.ZAux_Direct_GetOp.argtype = (c_void_p, c_int32, c_void_p)
        try:
            res = self.aux_api.ZAux_Direct_GetOp(self.handleRobot, c_int32(portPos), value)
        except Exception as e:
            res = -1
            errorInfo = "in get_output() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        if res != 0:
            self.errorMsg = "in get_output(): ZAux_Direct_GetOp() 执行失败"
        return res, value[:][0]

    def set_output(self, port, value):
        """
        输出口控制
        :param port:
        :param value:
        :return:
        """
        self.aux_api.ZAux_Direct_SetOp.restype = c_int32
        self.aux_api.ZAux_Direct_SetOp.argtype = (c_void_p, c_int32, c_uint32)
        try:
            res = self.aux_api.ZAux_Direct_SetOp(self.handleRobot, c_int32(port), c_uint32(value))
        except Exception as e:
            res = -1
            errorMsg = "in set_output() error: {}".format(str(e))
            print(errorMsg)
            self.logger.debug(errorMsg)
        if res != 0:
            self.errorMsg = "in set_output(): 输出口设置失败"
        return res

    def connect_zmc(self, ip):
        """
        裁切机控制器连接接口
        :return:
        """
        if isinstance(ip, type(" ")):
            ip = ip.encode('ascii')
        self.api.ZMC_OpenEth.restype = c_int64
        self.api.ZMC_OpenEth.argtype = (c_char_p, c_void_p)
        status = self.api.ZMC_OpenEth(ip, byref(self.handleRobot))
        if status == 0:
            self.errorMsg = ""
        else:
            self.errorMsg = "裁切机控制器连接失败"
        return status

    def close_zmc_conn(self):
        """
        关闭zmc连接
        :return:
        """
        self.api.ZMC_Close.restype = c_int32
        self.api.ZMC_Close.argtype = c_void_p
        status = self.api.ZMC_Close(self.handleRobot)
        return status

    def set_modbus_bit(self, pos, value):
        """
        设置modbus的位寄存器
        :param pos: 设置的bit位位置
        :param value:设置的值
        :return:
        """
        value = (c_uint8 * 1)(value)
        self.aux_api.ZAux_Modbus_Set0x.restype = c_int32
        self.aux_api.ZAux_Modbus_Set0x.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)

        try:
            res = self.aux_api.ZAux_Modbus_Set0x(self.handleRobot, c_uint16(pos), c_uint16(1), value)
        except Exception as e:
            res = -1
            print("in Zmc(set_modbus_bit)  error: {}".format(e))
            self.logger.debug("in  Zmc(set_modbus_bit), error: {}".format(e))

        if res != 0:
            self.errorMsg = "in set_modbus_bit() ZAux_Modbus_Set0x() 执行失败"

        return res

    def get_modbus_bit(self, start, num):
        """
        获取modbus_bit的bit位的值,返回一个列表,里面存放的是所有的bit位的结果值.
        :param start:获取的bit位的起始位置
        :param num:获取的bit位的个数
        :return:
        """
        value = (c_uint8 * num)(0)
        self.aux_api.ZAux_Modbus_Get0x.restype = c_int32
        self.aux_api.ZAux_Modbus_Get0x.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)

        try:
            res = self.aux_api.ZAux_Modbus_Get0x(self.handleRobot, c_uint16(start), c_uint16(num), value)
        except Exception as e:
            res = -1
            errorInfo = "in get_modbus_bit() error:{}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        if res != 0:
            self.errorMsg = "in get_modbus_bit() ZAux_Modbus_Get0x() 执行失败"
        return res, value[:]

    def get_axis_postion(self, axisCounts):
        """
         获取轴的坐标,从0轴开始
        :param axisCounts:轴的个数
        :return:返回轴的坐标的值
        """
        value = (c_float * axisCounts)()
        self.aux_api.ZAux_GetModbusDpos.restype = c_int32
        self.aux_api.ZAux_GetModbusDpos.argtype = (c_void_p, c_int32, c_void_p)
        try:
            res = self.aux_api.ZAux_GetModbusDpos(self.handleRobot, c_int32(axisCounts), value)
        except Exception as e:
            res = -1
            errorInfo = "in CbjZmc.get_axis_postion() error:{}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        if res != 0:
            self.errorMsg = "in CbjZmc.get_axis_postion(): ZAux_GetModbusDpos() 执行失败"

        return res, value[:]

    def set_modbus_byte(self, bytePos, value):
        """
        设置modbus_byte字节寄存器
        :param bytePos:
        :param value:
        :return:
        """
        value = (c_uint16 * 1)(value)
        self.aux_api.ZAux_Modbus_Set4x.restype = c_int32
        self.aux_api.ZAux_Modbus_Set4x.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)

        try:
            res = self.aux_api.ZAux_Modbus_Set4x(self.handleRobot, c_uint16(bytePos), c_uint16(1), value)
        except Exception as e:
            res = -1
            errorInfo = "in CbjZmc.set_modbus_byte() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
        if res != 0:
            self.errorMsg = "in CbjZmc.set_modbus_byte() ZAux_Modbus_Set4x 执行失败"

        return res

    def set_modbus_float(self, start, counts, values):
        """
        设置modbus_ieee()浮点数的额寄存器,可以设置number个数,然后value
        :param start:modbus_ieee的起点
        :param counts:设置的个数
        :param values:设置的结果值
        :return:
        """
        values = (c_float * counts)(*values)
        self.aux_api.ZAux_Modbus_Set4x_Float.restype = c_int32
        self.aux_api.ZAux_Modbus_Set4x_Float.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)

        try:
            res = self.aux_api.ZAux_Modbus_Set4x_Float(self.handleRobot, c_uint16(start), c_uint16(counts), values)
        except Exception as e:
            res = -1
            errorInfo = "in CbjZmc.set_modbus_float(),error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
        if res != 0:
            self.errorMsg = "in CbjZmc.set_modbus_float() ZAux_Modbus_Set4x_Float() 执行失败"

        return res

    def excute_zmc_cmd(self, cmd):
        """
        控制执行指令,跟用zdveloper的命令行获取的cmd命令状态是一样的效果
        :param cmd:输入的命令指示
        :return:
        """
        res = b" " * 256
        cmd = cmd.encode('ascii')
        self.api.ZMC_Execute.restype = c_uint32
        self.api.ZMC_Execute.argtype = (c_void_p, c_char_p, c_uint32, c_char_p, c_uint32)
        try:
            value = self.api.ZMC_Execute(self.handleRobot, cmd, 1000, res, 256)
        except Exception as e:
            errorInfo = "in CbjZmc.excute_zmc_cmd() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
            value = -1
        if value != 0:
            self.errorMsg = "in CbjZmc.excute_zmc_cmd() ZMC_Execute() 执行失败"
        return value, res.strip()[:-2]

    def move_abs_four_axis(self, movePos):
        """
        四轴绝对运动,同时移动四个轴,在急停状态下不能移动
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(0,1,2,3)")
        cmd = "moveabs(" + str("%.3f" % movePos[0]) + "," + str("%.3f" % movePos[1]) + "," + str("%.3f" % movePos[2]) + "," + str(
            "%.3f" % movePos[3]) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_x(self, dis):
        """
        裁切机移动X轴
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(0)")
        cmd = "pmove(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_y(self, dis):
        """
        裁切机移动Y轴
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(1)")
        cmd = "pmove(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_z(self, dis):
        """
        裁切机移动Z轴
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(2)")
        cmd = "pmove(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_w(self, dis):
        """
        裁切机移动W轴
        :param dis:移动距离一般是角度
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(3)")
        cmd = "pmove(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_abs_x(self, dis):
        """
        裁切机移动X轴,绝对移动
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(0)")
        cmd = "moveabs(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_abs_y(self, dis):
        """
        裁切机移动Y轴,绝对移动
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(1)")
        cmd = "moveabs(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_abs_z(self, dis):
        """
        裁切机移动Z轴,绝对移动
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(2)")
        cmd = "moveabs(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_abs_w(self, dis):
        """
        裁切机移动W轴,绝对移动
        :param dis:移动距离一般是角度
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(3)")
        cmd = "moveabs(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def get_modbus_float(self, start, counts):
        """
        获取modbus_ieee()的值,可以获取多个值
        :param start: modbus_ieee起始位置
        :param counts: 读取的数量
        :return:
        """
        value = (c_float * counts)()
        self.aux_api.ZAux_Modbus_Get4x_Float.restype = c_int32
        self.aux_api.ZAux_Modbus_Get4x_Float.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)

        try:
            res = self.aux_api.ZAux_Modbus_Get4x_Float(self.handleRobot, c_uint16(start), c_uint16(counts), value)
        except Exception as e:
            res = -1
            print(e)

        if res != 0:
            self.errorMsg = "ZAux_Modbus_Get4x_Float 执行失败"
        return res, value[:]

    def set_modbus_string(self, msg):
        """
        可以发送字符串,后期用来备用
        :param msg:
        :return:
        """
        maxStrNum = 12
        newMsg = msg
        if len(msg) < maxStrNum:
            n = int((maxStrNum - len(msg)) // 2)
            newMsg = " " * n + msg
        for i in range(0, maxStrNum + 1, 2):
            try:
                b = newMsg[i]
            except Exception as e:
                errorInfo = "in CbjZmc.set_modbus_string() error : {}".format(str(e))
                print(errorInfo)
                b = " "
                # 这里不用加日志
            try:
                a = newMsg[i + 1]
            except Exception as e:
                errorInfo = "in CbjZmc.set_modbus_string() error : {}".format(str(e))
                print(errorInfo)
                a = " "
            self.set_modbus_byte(1010 + int(i / 2), (ord(a) << 8) | ord(b))

    def reconnect_cbj_zmc(self):
        """
        重新连接裁切机控制器,直接在cbjZmc对象中进行连接,不要再写到produce中去了,它的连接它自己负责就可以了.
        :return:
        """
        try:
            self.close_zmc_conn()
        except Exception as e:
            errorInfo = "in CbjZmc.reconnect_cbj_zmc() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        status = self.connect_zmc(self.ip)
        if status == 0:
            # 连接成功以后再下发参数
            self.logger.debug("裁切机控制器连接成功.")
            res, info = self.set_cbj_zmc_params_to_device()
            if res == -1:
                return "error", info
        return "ok" if status == 0 else self.errorMsg

    def set_cbj_zmc_params_to_device(self):
        """
        加载裁切机的参数到底层控制器
        :return:
        """
        try:
            res, paramsDict = self.db.get_cbj_zmc_params(self.tableName)  # 先从数据库中获取裁切机的参数列表
            if res != "ok":
                return "error", paramsDict
            else:
                paramsDict.pop("key")
                if len(paramsDict.values()) == 0:
                    info = "数据库中无数据,先从底层获取到裁切机的参数数据"
                    print(info)
                    self.logger.debug(info)
                    return self.write_cbj_zmc_params_to_mogodb()
                else:
                    paramsList = self.get_cbj_params_value_list_by_dict(paramsDict)
                    if paramsList is None:
                        info = "裁切机下发参数失败,里面有None值"
                        print(info)
                        self.logger.debug(info)
                    else:
                        ret = self.set_modbus_float(0, len(paramsList), paramsList)
                        if ret != 0:
                            # 重新写一次
                            gevent.sleep(0.1)
                            ret = self.set_modbus_float(0, len(paramsList), paramsList)
                            if ret != 0:
                                info = "in CbjZmc.set_cbj_zmc_params_to_device() set_modbus_float(0,53) failed"
                                print(info)
                                self.logger.debug(info)
                                return -1, info
                        else:
                            info = "裁切机控制器参数下发成功!"
                            print(info)
                            self.logger.debug(info)
                            return 0, "参数下发成功!"
        except Exception as e:
            errorInfo = "in CbjZmc.set_cbj_zmc_params_to_device() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
            return -1, errorInfo

    @staticmethod
    def get_cbj_params_value_list_by_dict(paramsDict):
        """
        通过传入的字典,获取裁切机的参数列表
        :param paramsDict: 传入的字典
        :return:
        """
        paramsList = [paramsDict.get(key) for key in cbjZmcParamsKey]
        if None in paramsList:
            return None
        else:
            return paramsList

    def write_cbj_zmc_params_to_mogodb(self):
        """
        将裁切机的参数,写入到mogodb数据库中.
        :return:
        """
        ret, val = self.get_modbus_float(0, 53)
        if ret != 0:
            return "error", "in write_cbj_zmc_params_to_mogodb() 读取数据失败"
        else:
            cbjZmcParams = dict(zip(cbjZmcParamsKey, val))
            query = {
                "key": "1"
            }
            newValues = {
                "$set": cbjZmcParams
            }
            return self.db.update_data(self.tableName, query, newValues)

    def web_request_get_cbj_zmc_params(self):
        """
        获取裁切机的参数.
        :return:
        """
        try:
            res, paramsDict = self.db.get_cbj_zmc_params(self.tableName)
            if res != "ok":
                return "数据库错误,获取的数据为空"
            else:
                return [paramsDict]
        except Exception as e:
            errorInfo = "in CbjZmc.web_request_get_cbj_zmc_params() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

    def web_request_save_cbj_zmc_params(self, newParams):
        """
        裁切机参数界面上点击了保存
        :param newParams:
        :return:
        """
        try:
            query = {
                "key": "1"
            }
            newValues = {
                "$set": newParams
            }
            res, info = self.db.update_data(self.tableName, query, newValues)
            connected = self.check_connect()
            if connected:
                ret, info = self.set_cbj_zmc_params_to_device()
                if ret == -1:
                    return info
            return "ok" if res == "ok" else info
        except Exception as e:
            errorInfo = "in cbjZmc.web_request_save_cbj_zmc_params() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

    def write_cbj_angle_offset_from_zmc_to_mongo(self):
        """
        获取底层写的裁切机的角度补偿值,并写入到数据库
        :return:
        """
        connected = self.check_connect()
        if connected:
            ret, valList = self.get_modbus_float(110, 1)
            if ret != 0:
                return "error", "in write_cbj_angle_offset_from_zmc_to_mongo() 读取数据失败"
            else:
                newData = {
                    "knifeAngleOffset": valList[0]
                }
                return self.db.save_knife_cut_params(newData)
        else:
            return "error", "裁切机未连接"

    def set_cbj_angle_offset_from_mongo_to_zmc(self, value):
        """
        将裁切机的角度校准写入到底层
        :param value: 角度校准的值
        :return:
        """
        conencted = self.check_connect()
        if conencted:
            ret = self.set_modbus_float(110, 1, [value])
            if ret != 0:
                return "error", "in CbjZmc.set_cbj_angle_offset_from_mongo_to_zmc() error: {}".format(ret)
            else:
                return "ok", "写入成功"
        else:
            return "ok", "设备未连接"

    def cut_one_line_without_capfile(self, p1, p2, cutDeep):
        """
        不带台面补偿的切直线
        :param p1:直线的起点
        :param p2:直线的终点
        :param cutDeep:下刀深度
        :return:
        """
        self.set_table(60, 7, [p1[0], p1[1], cutDeep, 0, p2[0], p2[1], cutDeep])
        self.set_modbus_bit(10, 1)  # 下发完成切直线
        self.wait_cut_one_line_finish()  # 等待切割完成

    def wait_cut_one_line_finish(self):
        """
        裁切机等待切割直线完成,如果切割完成bit10 会变成0
        :return:
        """
        while True:
            res = self.get_modbus_bit(10, 1)[1][0]
            if res == 0:
                return 0
            gevent.sleep(0.05)

    def set_lines_to_cbj_with_capfile(self, lineList, speedList, cutDeep, boardIndex=0):
        """
        设置带台面补偿的切直线
        :param lineList: 切线的列表 [[p1,p2],[p3,p4],[p5,p6],[p7,p8]] 类似这种
        :param speedList: [speed1,speed2]
        :param cutDeep: 切割深度
        :return:
        """
        sections = 100
        newSpeedList = [len(lineList)]
        newSpeedList.extend(speedList)
        res = self.set_table(89, len(lineList) + 1, newSpeedList)
        if res != 0:
            return "error", "in CbjZmc.set_lines_to_cbj_with_capfile() error: 设置table数据失败"

        for index, line in enumerate(lineList):
            p1, p2 = line
            if len(self.capfileData) == 0:
                data = get_line_data_without_capfile(p1, p2, sections, cutDeep)
            else:
                data = get_line_data_with_capflie(p1, p2, sections, self.capfileData, self.xInterval, self.yInterval,
                                                  self.xMin, self.yMin, self.xNums, self.yNums, cutDeep)
            debugInfo = "id_{} 下发的第 {} 条线: {}".format(boardIndex, index + 1, line)
            print(debugInfo)
            self.logger.debug(debugInfo)
            res = self.set_table(400 * index + 100, 400, data)
            if res != 0:
                self.reconnect_cbj_zmc()
                self.logger.debug("id_{} 设置失败,重新连接裁切机,再设置一次".format(boardIndex))
                res = self.set_table(400 * index + 100, 400, data)
                if res != 0:
                    self.logger.debug("id_{} 设置失败".format(boardIndex))
                    return "error", "切线下发失败"
            else:
                res, newData = self.get_table(index * 400 + 100, 400)
                sumError = np.sum(np.subtract(data, newData))
                self.logger.debug("id_{} Set lines diff {:.4f}".format(boardIndex, sumError))
                if sumError > 2:
                    self.logger.debug("设置和读取的误差过大,重新设置一遍")
                    self.set_table(index * 400 + 100, 400, data)
                    res, newData = self.get_table(index * 400 + 100, 400)
                    sumError = np.sum(np.subtract(data, newData))
                    if sumError > 2:
                        return "error", "切线下发失败"
        return "ok", "切线下发成功"

    def web_request_save_capfile_data(self, capfileData):
        """
        前端界面点击了保存台布测量之后执行的逻辑
        :param capfileData: 要更新的数据
        :return:
        """
        if "" in capfileData:
            return "error ,台布测量的设置数据不能是空"
        xMin, yMin, xNums, yNums, xInterval, yInterval = capfileData
        res, table = self.get_table(100, int(xNums) * int(yNums) * 4)
        if res != 0:
            return "从底层获取数据失败,裁切机连接异常!"

        query = {
            "key": "1"
        }
        newValues = {
            "$set": {
                "capfileData": table,
                "xMin": float(xMin),
                "yMin": float(yMin),
                "xNums": int(xNums),
                "yNums": int(yNums),
                "xInterval": float(xInterval),
                "yInterval": float(yInterval),
            }
        }
        res, info = self.db.update_data(self.db.capfileTable, query, newValues)
        self.load_cafile_data_params()
        return "ok" if res == "ok" else info

    def web_request_test_plat_form(self):
        """
        前端界面点击开始测高
        :return:
        """
        ret = self.start_test_plat_form()
        return "ok" if ret == 0 else ret


if __name__ == '__main__':
    pass
